iT邦幫忙

2022 iThome 鐵人賽

DAY 6
2
Software Development

C# ASP.NET MVC實作: 30天打造屬於你的網站應用程式系列 第 6

(DAY 6)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-資料傳遞(三):表單(form)

  • 分享至 

  • xImage
  •  

今天我們來介紹form表單相關內容,前2天講到的資料傳遞都是藉由Controller從資料模型中抓取Data,再將Data丟到View呈現的方式。那如果是想把在View頁面輸入的資訊傳回給Controller,再做後續資料傳遞與處理的話該怎麼做呢?

例如我們想建立一個登入畫面,使用者可以輸入帳號密碼後按登入並顯示登入後畫面,這時候就會需要用到關鍵的HTML表單 (form)了!

因為之前的DemoController已經寫了太多方法,避免混亂我們另外新建一個Controller叫做LogInController,忘記怎麼做的話可以回去看Day3的內容~
建立後Code如下:

    public class LogInController : Controller
    {
        // GET: LogIn
        public ActionResult Index()
        {
            return View();
        }
    }

● 表單(form)建立

接著新增View,View的內容會使用HTML的form標籤,在View修改為如下列的Code:


@{
    ViewBag.Title = "Index";
}

<h2>LogIn Page</h2>

<div class="container">
    <form method="post" action="~/login/verify">
        <label>Account</label>
        <br />
        <input type="text" name="account" />
        <br /><br />
        <label>Password</label>
        <br />
        <input type="text" name="password" />
        <br /><br />
        <input type="submit" name="submit" value="Login" class="btn btn-primary" />
    </form>
</div>

完成後執行畫面如下:

簡單說明一下上面範例form標籤的語法,幾個重點如下:

  1. 要傳遞的資料內容一定要包在<form>標籤裡面才能傳遞。
  2. <form>標籤的method屬性決定用何種HTTP請求方法傳送,主要為post但也有get選項。
  3. <form>標籤的action屬性決定送出表單後,表單內容該送去哪個URL。
  4. 表單送出必須搭配submit按鈕,一個表單內只能有一個submit按鈕。
  5. 表單內的元素是透過name屬性繫結,才能讓後端對應到元素欄位。

另外提一下標籤裡面會看到class屬性,這是用來美化標籤樣式的功能,預設是套用Bootstrap套件的樣式檔,後面會專門開一篇來介紹。

● form對應動作方法建立

因為我們已經在form裡面給定了method使用post方法,以及action要傳送到~/login/verify 這個URL,代表我們在LoginController內就要有個對應的動作方法叫做Verify,且這個方法是接受HTTP POST的請求。因此我們新增如下Code:

        [HttpPost]
        public ActionResult Verify()
        {
            return View();
        }

注意動作方法的上方一定要標註[HttpPost] ,否則預設都會視為[HttpGet]

● 接收資料-FormCollection

接著說明form的內容如何繫結到動作方法,第一種是使用FormCollection來抓取表單內資料元素,因為資料量不大這邊直接使用ViewBag攜帶,我們將Verify()方法修改Code如下:

        [HttpPost]
        public ActionResult Verify(FormCollection obj)
        {
            ViewBag.Account = obj["account"]; 
            ViewBag.Password = obj["password"];

            return View();
        }

要注意的重點在於FormCollection使用form裡面元素的name屬性來繫結(不區分大小寫),對照下圖:

最後來新增Verify()方法對應的View,Code如下:


@{
    ViewBag.Title = "Verify";
}

<h2>Verify</h2>

<div>
    <ol>
        <li>User: @ViewBag.Account</li> 
        <li>Pwd: @ViewBag.Password</li>
    </ol>
</div>

執行的時候注意不是直接連到~/login/verify 這個URL,因為它是藉由POST方法請求,而直接輸入網址是一種GET方法,我們並沒有建立對應GET的動作方法。我們應該要從原本的~/login/Index 執行,輸入帳號與密碼欄位內容,並按下Submit按鈕提交表單,依照表單的methodaction屬性才會轉到~/login/verify並顯示對應的View。

按下Login後畫面如下:

● 接收資料-name元素繫結

另外一種方式是直接將參數名稱對應form裡面元素name屬性,並代入動作方法,修改Verify()方法Code如下:

        [HttpPost]
        public ActionResult Verify(string account, string password)
        {
            ViewBag.Account = account;
            ViewBag.Password = password;
            return View();
        }

View的部分不用修改,顯示結果會與上面相同。

● 接收資料-model

最後一種方式就是自行建立類別與屬性,來對應form裡面元素name屬性,首先新增一個LoginViewModel 類別於Models資料夾底下,並加入對照form的屬性AccountPassword,Code如下:

    public class LoginViewModel
    {
        public string Account { get; set; }
        public string Password { get; set; }
    }

接著修改Verify()方法Code如下:

        [HttpPost]
        public ActionResult Verify(LoginViewModel vm) //using Models
        {
            ViewBag.Account = vm.Account;
            ViewBag.Password = vm.Password;

            return View();
        }

View的部分一樣不用修改,顯示結果會與上面相同。

● form表單使用get方法

一開始我們有提到form表單其實可以使用get方法來提交資料,那呈現方式會是如何呢?
首先當然我們要先把View的<form>裡面的method屬性改為get,Code如下:


@{
    ViewBag.Title = "Index";
}

<h2>LogIn Page</h2>

<div class="container">
    <form method="get" action="~/login/verify">
        <label>Account</label>
        <br />
        <input type="text" name="account" />
        <br /><br />
        <label>Password</label>
        <br />
        <input type="text" name="password" />
        <br /><br />
        <input type="submit" name="submit" value="Login" class="btn btn-primary" />
    </form>
</div>

接著在Controller將Verify()動作方法上面的[HttpPost]拿掉或改成[HttpGet],Code如下:

        [HttpGet]
        public ActionResult Verify(LoginViewModel vm)
        {
            ViewBag.Account = vm.Account;
            ViewBag.Password = vm.Password;

            return View();
        }

重新執行Index畫面並登入,畫面如下:


眼睛比較利的人可以發現,當使用GET請求方式提交表單時,表單輸入的資訊會被放到URL的尾端,這與我們在Day3時講到的重點相同。這樣是否會有點問題呢?因為帳號密碼資訊不應該在網址上可以被看到才對呀!?所以透過這個例子可以瞭解,為何form通常會使用POST而不用GET的理由之一了,但原因不僅僅是這樣而已,下面會做個詳細比較整理。

※Day 3內容節錄:

● GET 與 POST 比較整理

在form表單使用POST的理由可能如下:
1. 安全性:
如上面範例所述,使用GET請求時把資料放在URL上傳遞是一目瞭然的,我們並不希望隱密的內容可以如此被直接看到。而使用POST請求時,傳遞內容會放在請求的message body裡面,這相對安全多了。(但實際上仍可透過查看HTTP封包內容得知)

2. 參數長度限制:
例如我們知道GET請求是將參數放在URL尾端傳送,但根據不同瀏覽器URL限制的長度會不同(ex: IE為2083 bytes、Chrome為8192 bytes),需要傳遞的參數過多可能會造成影響。而POST是將傳遞參數放在請求的message body 中,就沒有長度限制問題。

3. 為了新增或修改資源:
根本上來說,HTTP/1.1協定中定義的POST請求就是向指定資源提交資料,請求伺服器進行處理(例如提交表單或者上傳檔案)。這個請求可能會建立新的資源或修改現有資源,或二者皆有。

例如提交表單的目的是要新增會員資料、修改現有密碼、或者將商品加入購物車清單等等,這些動作會新增或變更資料庫內容,此時就應該使用POST方法;而如果發送表單只為了幫助查尋資源內容,那使用GET就無妨。

※這邊可以延伸出來講個名詞叫做冪等性(idempotency),冪等指的是,假如在不考慮諸如錯誤或者過期等問題的情況下,若干次請求的副作用與單次請求相同或者根本沒有副作用,那麼這些請求方法就能夠被視作「冪等(idempotence)」的。
例如GET請求是「冪等」的,因為它只讀取顯示內容而不會新增或修改到資源,同樣的請求即使不小心送了兩次,也不用擔心對伺服器端造成不同的影響。而POST請求則是「非冪等」的,因為每次提交表單都會對資源造成影響,如果User不小心送了兩次請求,就有可能造成重複提交表單,這也是網頁開發者需要去防範的問題。

GET 與 POST的詳細比較表格如下:

請求方式 GET POST
資料傳遞方式 將參數放在URL尾端,傳遞至Server 傳遞參數會放在請求的 message body 中
參數長度限制 URL長度限制根據瀏覽器、伺服器會有所不同。 長度無限制
安全性 傳遞的參數會在URL上顯示,較POST不安全 較GET安全,但實際上內容可透過HTTP封包看到
資料種類 只允許 ASCII,非ASCII會進行轉碼 無限制
重新載入 不影響顯示結果 會詢問重新提交資料
瀏覽器書籤 可以加入 無法加入
冪等性 冪等 非冪等

● 小結

今天主要講了form表單傳送資料的幾種方式,以及GET與POST的比較,明天來講和View畫面呈現息息相關的BootStrap套件功能,那就明天見囉~

※小弟不才,在軟體的世界還只是個小菜雞,如果內容有任何謬誤或問題,還請各位大神前輩們多多批評指教~歡迎下方留言討論^^


上一篇
(DAY 5)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-資料傳遞(二):物件Model
下一篇
(DAY 7)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-Bootstrap套件
系列文
C# ASP.NET MVC實作: 30天打造屬於你的網站應用程式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言